# -*- coding: utf-8 -*-


import requests
from concurrent.futures import ThreadPoolExecutor,as_completed  # 线程池模块
from bs4 import BeautifulSoup

"""
爬取前50页的文章标题以及详情页的链接
https://www.cnblogs.com/sitehome/p/2
"""
for i in range(1,51):
    url = f'https://www.cnblogs.com/sitehome/p/{i}'

# 推导式， 一次性生成50页的url
urls = [f'https://www.cnblogs.com/sitehome/p/{i}' for i in range(1,51)]
# print(urls)

# 获取网页源码
def get_html(url):
    res = requests.get(url)
    return res.text

# 解析数据
def parse(html):
    # 实例化对象
    soup = BeautifulSoup(html, 'lxml')
    links = soup.find_all('a', class_="post-item-title")
    lst = []  # 添加的是一页，还是全部？
    for link in links:
        href = link['href']
        title = link.text
        lst.append((href, title))  # 这里的lst是单页数据  循环一次 添加一组数据(一页)
    return lst


# 想让他自动关闭线程池，用上下文管理器
# max_workers= 用来设置最大的线程数量， 根据系统的情况选择一个合适线程数量
# with ThreadPoolExecutor() as pool:  # map
#     # get_html 是一个函数，用于处理爬取给定的url数据(源码)
#     # 是一个包含多个url的列表(序列)
#     # 会对urls列表当中每一个url调用get_html这个函数，每次调用都可能在线程池中不同的线程上执行
#     html1 = pool.map(get_html,urls)  # urls  多个
#     for html in html1:  # 循环一次  处理一页数据 利用parse的解析函数 去做处理  逐个处理
#         future = pool.submit(parse, html) # html每一页的网页源码， 单个任务的控制
#         # 获取结果  future对象  通过result()方法得到结果
#         print(future.result())


# 需求: 爬取当前页数据的时候，打印当前访问的url
# 创建两个线程  负责获取网页源码
with ThreadPoolExecutor() as pool:  # map
    # 得到的是一个生成器
    html1 = pool.map(get_html, urls) # urls
    # 打包zip  第一个url跟第一个网页源码组合在一起  第二个url跟第二个网页源码组合在一起
    html2 = list(zip(urls, html1)) #  url跟网页源码个数是对应的

# 解析数据
with ThreadPoolExecutor() as pool:
    # 依次从列表里面获取url跟网页源码
    dic = {}  # {'需求数据':'url'}
    for url, html in html2:  # parse函数的功能是干嘛？ 解析数据
        future = pool.submit(parse, html) # 每循环一次 函数只会作用在一个源码当中  数据对象
        # dic[数据对象] = url1  dic[数据对象] = url2
        dic[future] = url
    print(dic)

    # 循环字典，获取数据
    # for key, value in dic.items():
    #     print(key, value.result())

    # 哪一个任务先完成，先提取哪一个任务 只能获取键 字典循环只能获取键
    for future in as_completed(dic):
        url = dic[future]  # 获取字典当中的值  url
        # print(future)
        # print(url)
        print(url, future.result())
#









"""
函数三剑客: map, reduce, filter
map(func, seq): 第一个参数是给一个函数，第二个是给一个序列类型
map的返回结果是一个迭代器， 想看最终结果要转为list

需求: 我想要将列表序列中各个元素加1

匿名函数: 如果只有一个函数而且只有一个返回值
"""
# from functools import reduce
#
# list1 = [1,2,3,4,5]
# list2 = []
# for i in list1:
#     list2.append(i+1)
# print(list2)

# list1 = [-1,2,-3,4,-5]
#
# print(list(map(abs, list1)))

# def add1(x):
#     return '左手'

# print(list(map(add1, list1)))
# print(list(map(lambda x:x+1, list1)))

"""
lambda： x:x+1  冒号前面的x 是参数列表  x+1 就是你序列类型最终返回的结果(需求数据)
总结: 
1.map内置函数的作用是操作序列中的所有元素，并且返回一个迭代器，迭代器要转列表才能得到最终值
2.我们的lambda表达式可以专门配合我们的高阶内置函数来做简单实现

reduce(func,seq): 将每次函数计算的结果继续和序列当中的下一个元素做累加计算，最终结果只有一个值

注意：reduce传入的参数func必须接受两个参数
"""

# sum = 0
# for i in list1:
#     sum += i
# print(sum)

# def f(a,b):
#     return a+b
#
# res = reduce(f, list1)
# print(res)
#
# print(reduce(lambda a,b:a+b, list1))

"""
总结：
1. reduce就是平常遇见累积累加都可以用，当然累积也不一定要用reduce
2. reduce返回的是一个值
3. reduce需要导入 from functools import reduce
"""
